package cn.cloud9.chinese.mainland.util;

import cn.cloud9.chinese.mainland.constant.Constant;
import cn.cloud9.chinese.mainland.po.RegionCell;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.alibaba.druid.util.StringUtils;
import lombok.SneakyThrows;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

import static cn.cloud9.chinese.mainland.constant.Constant.*;

/**
 * @author OnCloud9
 * @description
 * @project RegionReptile-Remaster
 * @date 2022年07月07日 下午 10:01
 */
public class MyUtil {
    private static Db db = Db.use();
    /**
     * 分配新的请求头Header
     * @return
     */
    public static Map<String, String> getNewRequestHeader() {
        return new HashMap<String, String>(){{
            this.put("Host", "www.stats.gov.cn");
            this.put("Upgrade-Insecure-Requests", "1");
        }};
    }

    /**
     * 初始化表空间
     * @param tableName
     */
    @SneakyThrows
    public static void initialTableSpace(String tableName) {
        String SQL =
            "CREATE TABLE IF NOT EXISTS "+ tableName +" (\n" +
            "  `CODE` varchar(24) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '行政区代码',\n" +
            "  `PARENT_CODE` varchar(24) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '上级代码',\n" +
            "  `NAME` varchar(24) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '名称',\n" +
            "  `LINK` varchar(252) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '网页地址',\n" +
            "  `LEVEL` int DEFAULT NULL COMMENT '层级',\n" +
            "  `TYPE_CODE` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '类型',\n" +
            "  `GEN_TIME` datetime DEFAULT NULL COMMENT '创建时间',\n" +
            "  PRIMARY KEY (`CODE`) USING BTREE, \n" +
            "  KEY `IDX_LEVEL` (`LEVEL`) USING BTREE,\n" +
            "  KEY `IDX_PC` (`PARENT_CODE`) USING BTREE \n" +
            ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;";
        db.execute(SQL, null);
    }

    /**
     * 连接重试
     * @param httpRequest
     * @return
     */
    public static HttpResponse retryConn(HttpRequest httpRequest) {
        HttpResponse httpResponse = null;
        try {
            httpResponse = httpRequest
                .timeout(TIMEOUT)
                .setConnectionTimeout(TIMEOUT)
                .setReadTimeout(TIMEOUT)
                .execute();
        } catch (Exception exception) {
            exception.printStackTrace();
            return retryConn(httpRequest);
        }
        return httpResponse;
    }

    /**
     * 写入操作封装, 不更新，
     * 有CODE主键发生冲突，异常后执行下一个
     * @param tableName
     * @param cell
     */
    @SneakyThrows
    public static void writeDataToDb(String tableName, RegionCell cell) {
        db.insert(Entity
            .create(tableName)
            .set(CODE, cell.getCode())
            .set(PARENT_CODE, cell.getParentCode())
            .set(NAME, cell.getName())
            .set(LEVEL, cell.getLevel())
            .set(TYPE_CODE, cell.getTypeCode())
            .set(LINK, cell.getLink())
            .set(GEN_TIME, cell.getGenTime()));
    }

    /**
     * 村，居委会读取
     * @param document
     * @param newCell
     * @param superCell
     * @param tableName
     */
    public static void readVillage(
        Document document,
        RegionCell newCell,
        RegionCell superCell,
        String tableName
    ) {
        Elements villageTrList = document.getElementsByClass("villagetr");
        if (CollUtil.isEmpty(villageTrList)) return;
        villageTrList.forEach(villageTr -> {
            Elements villageTdList = villageTr.getElementsByTag("td");
            newCell.setCode(villageTdList.get(0).text());
            newCell.setParentCode(superCell.getCode());
            newCell.setName(villageTdList.get(2).text());
            newCell.setTypeCode(villageTdList.get(1).text());
            newCell.setLevel(5);
            newCell.setGenTime(LocalDateTime.now());
            writeDataToDb(tableName, newCell);
        });
    }

    /**
     * 街道，镇 读取
     * @param BASE_URL
     * @param document
     * @param newCell
     * @param superCell
     * @param tableName
     */
    public static void readTown (
        String BASE_URL,
        Document document,
        RegionCell newCell,
        RegionCell superCell,
        String tableName
    ) {
        Elements townTrList = document.getElementsByClass("towntr");
        if (CollUtil.isEmpty(townTrList)) return;
        townTrList.forEach(townTr -> {

            Elements townTds = townTr.getElementsByTag("td");
            Element townCodeTd = townTds.get(0);
            Element townNameTd = townTds.get(1);
            Elements aTagsInTownCodeTd = townCodeTd.getElementsByTag("a");
            Elements aTagsInTownNameTd = townNameTd.getElementsByTag("a");
            // 取城乡代码
            String regionTownCode;
            String townNextHref = null;

            boolean isTownLinkTag = !CollUtil.isEmpty(aTagsInTownCodeTd);
            if (isTownLinkTag) {
                regionTownCode = aTagsInTownCodeTd.get(0).text();
                townNextHref = aTagsInTownCodeTd.get(0).attr("href");
            } else regionTownCode = townCodeTd.text();
            String regionTownName;
            if (!CollUtil.isEmpty(aTagsInTownNameTd)) regionTownName = aTagsInTownNameTd.get(0).text();
            else regionTownName = townNameTd.text();

            final String codePrefix = regionTownCode.substring(0, 2);
            boolean condition1 = "44".equals(codePrefix) || "46".equals(codePrefix);

            if (condition1 && isTownLinkTag) {
                final String link = superCell.getLink();
                final String basePath = link.substring(0, link.lastIndexOf(Constant.PATH_CHAR) + 1);
                String url = basePath + townNextHref;
                newCell.setLink(url);
            } else if (isTownLinkTag) {
                int index = townNextHref.indexOf(Constant.PATH_CHAR);
                String provincePath = townNextHref.substring(index + 1, index + 3);
                String cityPath = Constant.PATH_CHAR + townNextHref.substring(index + 3, index + 5) + Constant.PATH_CHAR;
                String url = BASE_URL + provincePath + cityPath + townNextHref;
                newCell.setLink(url);
            }

            newCell.setCode(regionTownCode);
            newCell.setParentCode(superCell.getCode());
            newCell.setName(regionTownName);
            newCell.setGenTime(LocalDateTime.now());
            newCell.setLevel(4);
            writeDataToDb(tableName, newCell);
            readDataRecursive(newCell, BASE_URL, tableName);
        });
    }

    /**
     * 区县读取
     * @param BASE_URL
     * @param document
     * @param newCell
     * @param superCell
     * @param tableName
     */
    public static void readCounty (
        String BASE_URL,
        Document document,
        RegionCell newCell,
        RegionCell superCell,
        String tableName
    ) {
        Elements countyTrList = document.getElementsByClass("countytr");
        if (CollUtil.isEmpty(countyTrList)) return;
        countyTrList.forEach(countyTr -> {
            Elements countyTds = countyTr.getElementsByTag("td");
            Element countyCodeTd = countyTds.get(0);
            Element countyNameTd = countyTds.get(1);
            Elements aTagsInCountyCodeTd = countyCodeTd.getElementsByTag("a");
            Elements aTagsInCountyNameTd = countyNameTd.getElementsByTag("a");
            // 取城乡代码
            String regionCountyCode;
            String countyNextHref = null;

            boolean isCountyLinkTag = !CollUtil.isEmpty(aTagsInCountyCodeTd);
            if (isCountyLinkTag) {
                regionCountyCode = aTagsInCountyCodeTd.get(0).text();
                countyNextHref = aTagsInCountyCodeTd.get(0).attr("href");
            } else regionCountyCode = countyCodeTd.text();

            String regionCountyName;
            if (!CollUtil.isEmpty(aTagsInCountyNameTd)) regionCountyName = aTagsInCountyNameTd.get(0).text();
            else regionCountyName = countyNameTd.text();

            if (isCountyLinkTag) {
                int index = countyNextHref.indexOf(Constant.PATH_CHAR);
                String provincePath = countyNextHref.substring(index + 1, index + 3) + Constant.PATH_CHAR;
                String countyUrl = BASE_URL + provincePath + countyNextHref;
                newCell.setLink(countyUrl);
            }

            newCell.setCode(regionCountyCode);
            newCell.setParentCode(superCell.getCode());
            newCell.setName(regionCountyName);
            newCell.setGenTime(LocalDateTime.now());
            newCell.setLevel(3);
            writeDataToDb(tableName, newCell);
            readDataRecursive(newCell, BASE_URL, tableName);
        });
    }

    /**
     * 城市读取
     * @param BASE_URL
     * @param document
     * @param newCell
     * @param superCell
     * @param tableName
     */
    public static void readCity (
            String BASE_URL,
            Document document,
            RegionCell newCell,
            RegionCell superCell,
            String tableName
    ) {
        final Elements citytrList = document.getElementsByClass("citytr");
        if (CollUtil.isEmpty(citytrList)) return;
        citytrList.forEach(cityTr -> {
            Elements cityTds = cityTr.getElementsByTag("td");
            Element codeTd = cityTds.get(0);
            Element nameTd = cityTds.get(1);
            Elements asTagInCodeTd = codeTd.getElementsByTag("a");
            Elements asTagInNameTd = nameTd.getElementsByTag("a");

            String regionCityCode;
            String cityNextHref = null;

            boolean isLinkTag = !CollUtil.isEmpty(asTagInCodeTd);
            if (isLinkTag) {
                regionCityCode = asTagInCodeTd.get(0).text();
                cityNextHref = asTagInCodeTd.get(0).attr("href");
            } else
                regionCityCode = codeTd.text();
            String regionName;
            if (!CollUtil.isEmpty(asTagInNameTd)) regionName = asTagInNameTd.get(0).text();
            else regionName = nameTd.text();

            if (isLinkTag) newCell.setLink(BASE_URL + cityNextHref);

            newCell.setCode(regionCityCode);
            newCell.setParentCode(superCell.getCode());
            newCell.setName(regionName);
            newCell.setGenTime(LocalDateTime.now());
            newCell.setLevel(2);
            writeDataToDb(tableName, newCell);
            readDataRecursive(newCell, BASE_URL, tableName);
        });
    }

    /**
     * 递归请求调用
     * @param regionCell
     * @param BASE_URL
     * @param tableName
     */
    @SneakyThrows
    public static void readDataRecursive(RegionCell regionCell, String BASE_URL, String tableName) {
        final String regionCellLink = regionCell.getLink();
        if (StringUtils.isEmpty(regionCellLink)) return;

        final HttpRequest httpRequest = HttpUtil
                .createGet(regionCellLink)
                .timeout(TIMEOUT)
                .setConnectionTimeout(TIMEOUT)
                .setReadTimeout(TIMEOUT);
        if (!CollUtil.isEmpty(regionCell.getCookies())) {
            final Map<String, String> headers = MyUtil.getNewRequestHeader();
            regionCell.getCookies().forEach( cookie -> headers.put("Cookie", cookie.toString() + ";"));
            headers.put(REFERER,  regionCell.getLink());
            headers.put(USER_AGENT, BROWSER_AGENTS[Constant.R.nextInt(BROWSER_AGENTS.length)]);
            headers.put("Host", "www.stats.gov.cn");
            headers.put("Upgrade-Insecure-Requests", "1");
            httpRequest.addHeaders(headers);
        }

        final HttpResponse httpResponse = retryConn(httpRequest);
        // 封装cookie给下一次请求使用
        final RegionCell.RegionCellBuilder builder = RegionCell.builder();
        builder.cookies(httpResponse.getCookies());
        final RegionCell newCell = builder.build();

        if (!httpResponse.isOk()) {
            System.out.println(httpResponse.body());
            return;
        }
        final Document document = Jsoup.parse(httpResponse.body());
        readCity(BASE_URL, document, newCell, regionCell, tableName);
        readCounty(BASE_URL, document, newCell, regionCell, tableName);
        readTown(BASE_URL, document, newCell, regionCell, tableName);
        readVillage(document, newCell, regionCell, tableName);
        return;
    }

    /**
     * 读取省份数据
     * @param BASE_URL
     * @param tableName
     */
    public static void readProvinceData(String BASE_URL, String tableName) {
        final String s = HttpUtil.get(BASE_URL);
        final Elements provincetrs = Jsoup.parse(s).getElementsByClass("provincetr");
        provincetrs.forEach(tr -> {
            Elements provinceas = tr.getElementsByTag("a");
            provinceas.forEach(a -> {
                RegionCell cell = RegionCell.builder()
                    .name(a.text())
                    .code( a.attr("href").replace(".html", ""))
                    .link(BASE_URL + a.attr("href"))
                    .parentCode(String.valueOf(0))
                    .genTime(LocalDateTime.now())
                    .level(1)
                    .build();
                MyUtil.writeDataToDb(tableName, cell);
                readDataRecursive(cell, BASE_URL, tableName);
            });
        });
    }

}
